package org.biogroovy.search.eutils

import javax.xml.parsers.DocumentBuilderFactory

import org.biogroovy.eutils.EUtilsURLFactory;
import org.biogroovy.io.eutils.PubMedArticleReader;
import org.biogroovy.models.Article
import org.biogroovy.models.MeshHeading
import org.biogroovy.search.IResultBuilder
import org.biogroovy.search.SearchException
import org.biogroovy.search.SearchParam
import org.biogroovy.search.SearchParamType
import org.biogroovy.search.SearchResult


/**
 * This class is responsible for performing PubMed searches.
 */
public class PubMedSearchEngine extends AbsEntrezSearchEngine {
	
	/** The name of the search engine */
	private static final String NAME = "PubMed";
	
	/** The URL of the pubmed server */
	private static final String URL = "http://www.ncbi.nlm.nih.gov/pubmed/";
	
	/**
	 * Constructor.
	 */
	public PubMedSearchEngine(){
		super();
		resultBuilder = new PubMedSearchResultBuilder();
	}
	
	@Override
	public String getName() {
		return NAME;
	}

	@Override
	public String getURLTemplate() {
		return EUtilsURLFactory.getURL(EUtilsURLFactory.ESEARCH, ["db":EUtilsURLFactory.DB_PUBMED])
	}

	@Override
	public List<SearchResult> doSearch(Map<String, String> parameters)
			throws SearchException {
					
		List<SearchResult<Article>> results = new ArrayList<>();	
		
		// do the initial search
		def builder  = DocumentBuilderFactory.newInstance().newDocumentBuilder();
		def root     = builder.parse(bind(parameters)).documentElement
		
		PubMedArticleReader reader = new PubMedArticleReader();
		List<String> idList = reader.parseList(root, "//Id");
		
		// fetch the articles
		String fetchURL = EUtilsURLFactory.getURL(EUtilsURLFactory.EFETCH, [db:EUtilsURLFactory.DB_PUBMED]);	
		List<Article> articleList = reader.readList(bind(fetchURL,[id:idList.join(","), retmode:'xml']));
		
		SearchResult<Article> result = null;
		IResultBuilder<Article> resultBuilder = getResultBuilder();
		for(Article article: articleList){
			result = resultBuilder.convert(article)
			results.add(result);
		}
		

		return results;
	}
		
	@Override
	protected void initSearchParams(){
		SearchParam<String> term = new SearchParam(name:'Term', dataName:'term', isTerm:true);
		term.setHint("The search term");
		
		SearchParam<String> sym = new SearchParam(name:'Symbol', isSubTerm:true);
		sym.setHint("The gene symbol");
		
		SearchParam<String> journal = new SearchParam(name:'Journal', isSubTerm:true);
		journal.setHint("The journal name");
		
		SearchParam<String> author = new SearchParam(name:'Author', isSubTerm:true);
		author.setHint("The author's name");
		
		SearchParam<Date> startDate = new SearchParam(name:'Start Date', dataName:'PDat', isSubTerm:true, paramType:SearchParamType.DATE);
		startDate.setHint("The earliest date to search for");
		
		SearchParam<Date> endDate = new SearchParam(name:'End Date', dataName:'PDat', isSubTerm:true, paramType:SearchParamType.DATE);
		endDate.setHint("The latest date to search for");
		
		SearchParam<String> pubType = new SearchParam(name:'Publication Type', isSubTerm:true);
		pubType.setHint("The publication type");
		pubType.setAllowableValues("review","clinical trial", "clinical trial, phase i",
			"clinical trial, phase ii", "clinical trial, phase iii", 
			"clinical trial, phase iv", "controlled clinical trial");
		
		SearchParam<String> pharmAction = new SearchParam(name:'Pharmacological Action', isSubTerm:true);
		pharmAction.setHint("The pharmacological method of action");
		pharmAction.setAllowableValues("angiogenesis inhibitors", "antineoplastic agents",
			"adjuvants, immunologic","adjuvants, pharmaceutic","antibiotics, antineoplastic",
			"antimutagentic agents","aromatase inhibitors", "carcinogens","carcinogens, environmental","caspase inhibitors",
			"cross linking reagents", "cyclooxygenase inhibitors", "cyclooxygenase 2 inhibitors","enzyme inhibitors",
			"enzyme activators","enzyme reactivators","matrix metalloproteinase inhibitors","peroxisome proliferators",
			"protein kinase inhibitors"
			)
		
		SearchParam<String> titleAbs = new SearchParam(name:'Title/Abstract',isSubTerm:true);
		titleAbs.setHint("The title or abstract");
		
		SearchParam<String> affiliation = new SearchParam(name:'Affiliation',isSubTerm:true);
		affiliation.setHint("The author(s) institutional affiliation");

// TODO: need to figure out a way to handle boolean types		
//		SearchParam<String> freeText = new SearchParam(name:'Free Full Text', dataName:'filter', isSubTerm:true, paramType:SearchParamType.STRING, value:'free fulltext');
		
		getSearchParameters().addAll(term,sym,journal, author, startDate, endDate, pubType, titleAbs, affiliation);
	}
	
	

	
	
	/**
	 * This class converts articles into search results.
	 * 
	 *
	 */
	class PubMedSearchResultBuilder implements IResultBuilder<Article>{
		
		/**
		 * Constructor.
		 */
		public PubMedSearchResultBuilder(){
			
		}
		
		@Override
		SearchResult<Article> convert(Article article){
			SearchResult<Article> result = new SearchResult([title:article.title, description:article.abs]);
			result.url = URL + article.pubmedId
			result.accession = article.pubmedId;
			result.authors = article.authors;
			result.source = article?.journal.toIdString();
			result.tags = new ArrayList();
			article.meshHeadings.each {MeshHeading heading ->
				result.tags.add(heading.descriptorName);
			}
			article.keywords.each{String keyword ->
				result.tags.add(keyword);
			}
			result.setResult(article);
			return result;
		}
	}

}
